/// https://leetcode-cn.com/problems/two-sum
pub struct Solution;

impl Solution {
    pub fn iterator(nums: Vec<i32>, target: i32) -> Vec<i32> {
        let mut index: usize = 0;
        let pos = nums.iter()
                      .enumerate()
                      .find_map(|(i, num)| {
                          index = i;
                          nums[i + 1..].iter().position(|&x| x == target - num)
                      })
                      .unwrap()
                  + index
                  + 1;
        vec![index as i32, pos as i32]
    }

    pub fn iterator_match_pattern(nums: Vec<i32>, target: i32) -> Vec<i32> {
        for (i, num) in nums.iter().enumerate() {
            let mut iter = nums[(i + 1)..].iter();
            match iter.position(|&x| x == target - num) {
                Some(p) => return vec![i as i32, (p + 1 + i) as i32],
                None => (),
            }
        }

        panic!() // unreachable!()
    }

    pub fn simple_for(nums: Vec<i32>, target: i32) -> Vec<i32> {
        for i in 0..nums.len() {
            for j in i + 1..nums.len() {
                if nums[i] + nums[j] == target {
                    return vec![i as i32, j as i32];
                }
            }
        }

        panic!() // unreachable!()
    }

    /// https://rustgym.com/leetcode/1
    pub fn hashmap(nums: Vec<i32>, target: i32) -> Vec<i32> {
        use std::collections::HashMap;

        let mut hashmap: HashMap<i32, i32> = HashMap::new();
        for (i, &num) in nums.iter().enumerate() {
            if let Some(&j) = hashmap.get(&(target - num)) {
                return vec![j, i as i32];
            } else {
                hashmap.insert(num, i as i32);
            }
        }

        panic!() // unreachable!()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn assert<F>(two_sum: F)
        where F: Fn(Vec<i32>, i32) -> Vec<i32> {
        let res = two_sum(vec![2, 7, 11, 15], 9);
        assert_eq!(res, vec![0, 1]);

        let res = two_sum(vec![2, 7, 11, 15], 26);
        assert_eq!(res, vec![2, 3]);

        let res = two_sum(vec![3, 2, 4], 6);
        assert_eq!(res, vec![1, 2]);

        let res = two_sum(vec![3, 3], 6);
        assert_eq!(res, vec![0, 1]);
    }

    bench!(assert Solution::hashmap);
    bench!(assert Solution::simple_for);
    bench!(assert Solution::iterator_match_pattern);
    bench!(assert Solution::iterator);
}
